/*
 * Tencent is pleased to support the open source community by making wechat-matrix available.
 * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
 * Licensed under the BSD 3-Clause License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://opensource.org/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//
// LintEnv's duty is to offer infos to the checkers
// eg.
// All the tables info including columns info, index info.. See GetTablesInfo;
// A api GetQuery that the checker can do a sql query to get the infos needed
// And so on.
//
// Author: liyongjie
// Created by liyongjie
//

#ifndef SQLITE_LINT_CORE_SQLITE_LINT_ENV_H
#define SQLITE_LINT_CORE_SQLITE_LINT_ENV_H

#include <vector>
#include <set>
#include <map>
#include <mutex>
#include "core/lint_info.h"
#include "core/white_list_mgr.h"

namespace sqlitelint {
    // Some sqls or tables aren't generated by the app.
    // These are so called reserve, including the sqls or tables used by SQLite3 or SQLiteLint
    class ReserveSqlManager {
    public:
        ReserveSqlManager() = default;
        void MarkReserve(const std::string& sql);
        // Is weird way to judge whether the sql is executed by SQLiteLint
        // The sql is marked in GetQuery(also see MarkReserve), and the mark has a lifetime see kMarkReserveIntervalThreshold
        // In this way, reserve_sql_map_ keeps not growing
        bool IsReserve(const std::string& sql);
        static bool IsReservedTable(const std::string& table_name);
    private:
        constexpr static const int kMarkReserveIntervalThreshold = 1000;

        std::map<std::string, int64_t> reserve_sql_map_;
    };

    class LintEnv {
    public:
        explicit LintEnv(std::string db_path);
        //~LintEnv();

        // Use this api to query the observed db
        // And as the query_sql is need by SQLiteLint own logic, it will not be checked
        int GetQuery(const std::string& query_sql, const SqlExecutionCallback& callback
                , void *para, char **errMsg);

        const std::vector<TableInfo> GetTablesInfo();

        void GetTableInfo(const std::string& table_name, TableInfo& tableInfo);

        int GetExplainQueryPlan(const std::string& sql, QueryPlan *query_plan);

        //Keep the sql execution history; See link.cc
        void AddToSqlHistory(const SqlInfo sql_info);
        const std::vector<SqlInfo>& GetSqlHistory();
        void ReleaseHistory(int count);
        void CheckReleaseHistory();

        const std::string GetDbPath() const;

        const std::string GetDbFileName() const;


        bool IsReserveSql(const std::string& sql);

        void SetWhiteList(const std::map<std::string, std::set<std::string>>& white_list);

        bool IsInWhiteList(const std::string& checker_name, const std::string& target) const;
        void IncSqlCnt();
        int GetSqlCnt();
    private:
        static constexpr const char* const kSelectTablesSql = "select name, sql from sqlite_master where type='table'";

        // one env is bound to one concerned db
        std::string db_path_;
        std::string db_file_name_;
        ReserveSqlManager reserve_sql_mgr_;
        std::vector<TableInfo> tables_info_;
        // only keep the simple info. eg. the parser tree is not included
        std::vector<SqlInfo> sql_history_;
        WhiteListMgr white_list_mgr_;
        int checked_sql_cnt_;
        std::mutex lints_mutex_;

        void CollectTablesInfo();

        static int SQLite3ExecSql(
                const char *db_path, /* Database filename (UTF-8)*/
                const char *sql, /* SQL to be evaluated */
                const SqlExecutionCallback& callback,  /* Callback function */
                void *, /* 1st argument to callback */
                char **errmsg /* Error msg written here */
        );
    };
}
#endif //SQLITE_LINT_CORE_SQLITE_LINT_ENV_H
